Before performing the analysis, several R-based packages should be installed on your local machine. Note that the packages may require an additional installation of other dependencies. Make sure that you have installed recommended version of the packages and your machine meets the mimimal system requirements for carrying out this analysis.
library(scater)
library(M3Drop)
library(monocle)
library(Seurat)
library(mclust)
library(scran)
library(SC3)
Zeisel2015 is a UMI-based scRNAseq dataset published in Zeisel et al. (2015) (Link to study: http://science.sciencemag.org/content/347/6226/1138) that contains the expression matrix of endogenous, spike-in and mitochondrial genes from 3005 cells from mouse cortex and hippocampus tissues. Additional metadata information are available, including cells origin, type or tissue. For the purpose of this tutorial we stored Zeisel2015 count matrix with metadata information in two separate txt files. We can read these files and create an easy-to-work SingleCellExperiment experiment object.
all.counts <- read.table(file ="data/all.counts.txt", sep = "\t")
metadata <- read.table(file ="data/metadata.txt", sep = "\t", header = TRUE)
sce <- SingleCellExperiment(list(counts=as.matrix(all.counts)), colData=metadata)
sce
## class: SingleCellExperiment
## dim: 19896 3005
## metadata(0):
## assays(1): counts
## rownames(19896): Tspan12 Tshz1 ... ERCC-00170 ERCC-00171
## rowData names(0):
## colnames(3005): V3 V4 ... V3006 V3007
## colData names(10): tissue group ... level1class level2class
## reducedDimNames(0):
## spikeNames(0):
We can explore the information about cells and genes stored in SingleCellExperiment experiment object under colData and rawData slots, respectively.
counts(sce)[1:4,1:4]
## V3 V4 V5 V6
## Tspan12 0 0 0 3
## Tshz1 3 1 0 2
## Fnbp1l 3 1 6 4
## Adamts15 0 0 0 0
names(colData(sce)) #available metadata information
## [1] "tissue" "group" "mRNA" "well" "sex"
## [6] "age" "diameter" "cellId" "level1class" "level2class"
table(colData(sce)$tissue) #tissue annotation
##
## ca1hippocampus sscortex
## 1314 1691
table(colData(sce)$level1class) #cell type annotation
##
## astrocytes_ependymal endothelial-mural interneurons
## 224 235 290
## microglia oligodendrocytes pyramidal CA1
## 98 820 939
## pyramidal SS
## 399
names(rowData(sce))
## character(0)
As mentioned, Zeisel2015 dataset contains also the expression of spike-ins and mitochondrial genes. Spike-in gene names usually starts from “ERCC” and mitochondrial genes contain “mt” string.
is.spike <- grepl("^ERCC", rownames(sce))
summary(is.spike)
## Mode FALSE TRUE
## logical 19839 57
is.mito <- grepl("^mt-", rownames(sce))
summary(is.mito)
## Mode FALSE TRUE
## logical 19862 34
We can use scater package to automatically calculate several quality metrics per cell and per gene. These metrics will be useful to i.e. detect low quality cells or lowly expressed genes that should be removed from further analysis.
sce <- calculateQCMetrics(sce, feature_controls=list(Spike=is.spike, Mt=is.mito))
colnames(colData(sce))
## [1] "tissue"
## [2] "group"
## [3] "mRNA"
## [4] "well"
## [5] "sex"
## [6] "age"
## [7] "diameter"
## [8] "cellId"
## [9] "level1class"
## [10] "level2class"
## [11] "is_cell_control"
## [12] "total_features_by_counts"
## [13] "log10_total_features_by_counts"
## [14] "total_counts"
## [15] "log10_total_counts"
## [16] "pct_counts_in_top_50_features"
## [17] "pct_counts_in_top_100_features"
## [18] "pct_counts_in_top_200_features"
## [19] "pct_counts_in_top_500_features"
## [20] "total_features_by_counts_endogenous"
## [21] "log10_total_features_by_counts_endogenous"
## [22] "total_counts_endogenous"
## [23] "log10_total_counts_endogenous"
## [24] "pct_counts_endogenous"
## [25] "pct_counts_in_top_50_features_endogenous"
## [26] "pct_counts_in_top_100_features_endogenous"
## [27] "pct_counts_in_top_200_features_endogenous"
## [28] "pct_counts_in_top_500_features_endogenous"
## [29] "total_features_by_counts_feature_control"
## [30] "log10_total_features_by_counts_feature_control"
## [31] "total_counts_feature_control"
## [32] "log10_total_counts_feature_control"
## [33] "pct_counts_feature_control"
## [34] "pct_counts_in_top_50_features_feature_control"
## [35] "pct_counts_in_top_100_features_feature_control"
## [36] "pct_counts_in_top_200_features_feature_control"
## [37] "pct_counts_in_top_500_features_feature_control"
## [38] "total_features_by_counts_Spike"
## [39] "log10_total_features_by_counts_Spike"
## [40] "total_counts_Spike"
## [41] "log10_total_counts_Spike"
## [42] "pct_counts_Spike"
## [43] "pct_counts_in_top_50_features_Spike"
## [44] "pct_counts_in_top_100_features_Spike"
## [45] "pct_counts_in_top_200_features_Spike"
## [46] "pct_counts_in_top_500_features_Spike"
## [47] "total_features_by_counts_Mt"
## [48] "log10_total_features_by_counts_Mt"
## [49] "total_counts_Mt"
## [50] "log10_total_counts_Mt"
## [51] "pct_counts_Mt"
## [52] "pct_counts_in_top_50_features_Mt"
## [53] "pct_counts_in_top_100_features_Mt"
## [54] "pct_counts_in_top_200_features_Mt"
## [55] "pct_counts_in_top_500_features_Mt"
colData(sce)$total_counts[1:5] #Total count for each cell
## [1] 29060 29304 38929 40557 27625
colData(sce)$total_features_by_counts[1:5] #Number of genes with non zero counts per cell
## [1] 4898 4750 6090 5887 4753
colData(sce)$pct_counts_Spike[1:5] #Percentage of spike-in counts per cell
## [1] 23.07639 21.95946 16.27322 17.33856 21.46968
rowData(sce)$mean_counts[1:5] #Average count per gene
## [1] 0.27886855 0.42362729 1.04292845 0.04459235 0.37936772
rowData(sce)$n_cells_by_counts[1:5] #Number of cells with non zero counts per gene
## [1] 472 573 1167 77 669
rowData(sce)$pct_dropout_by_counts[1:5] #Percentage of dropouts per gene
## [1] 84.29285 80.93178 61.16473 97.43760 77.73710
One can plot histograms based on quality metrics to determine possible thresholds for cell/gene filterings.
hist(sce$total_counts, breaks=50, xlab="Library size", ylab="Number of cells")
hist(sce$total_features_by_counts, xlab="Number of expressed genes", breaks=50, ylab="Number of cells")
hist(sce$pct_counts_Spike, xlab="ERCC proportion (%)", ylab="Number of cells", breaks=50)
Alternatively, isOutlier function provides an automatic detection of low quality cells based on quality metrics. In this example, we are filtering cells based on library size, number of expressed genes per cell and total count over all spike-in transcripts in each cell. Excluded cells are those with the total number of expressed genes and the total sum of counts more than 3 median absolute deviations below the median across the genes.
libsize.drop <- isOutlier(sce$total_counts, nmads=3, type="lower", log=TRUE)
feature.drop <- isOutlier(sce$total_features_by_counts, nmads=3, type="lower", log=TRUE)
spike.drop <- isOutlier(sce$pct_counts_Spike, nmads=3, type="higher")
sce <- sce[,!(libsize.drop | feature.drop | spike.drop)]
data.frame(ByLibSize=sum(libsize.drop), ByFeature=sum(feature.drop), BySpike=sum(spike.drop), Remaining=ncol(sce))
## ByLibSize ByFeature BySpike Remaining
## 1 8 3 8 2989
We can also explore highly and overexpressed genes. The overexpressed genes, are those genes which expression is higher than others by several magnitudes. Overexpressed genes can bias further procedures such as clustering, therefore, they should be removed before performing downstream analysis. Note that highly expressed genes mostly include spike-ins and mitochondrial genes. One of the most commonly overexpressed endogenous gene is “Malat1”.
plotHighestExprs(sce, exprs_values = "counts")
In this step we will filter spike-in and mitochondrial genes as well as “Malat1”.
sce=sce[-which(grepl("^ERCC-", rownames(sce))),]
sce=sce[-which(grepl("^mt-", rownames(sce))),]
sce <- sce[-which(rownames(sce)=="Malat1"),]
dim(sce)
## [1] 19804 2989
Now, we should filter out lowly expressed genes as they do not provide any insights into the underlying biology. In the filtering we removed lowly expressed genes that are genes with average expression count (adjusted by library size) equal to 0.
rowData(sce)$ave.counts <- calcAverage(sce, exprs_values = "counts", use_size_factors=FALSE)
to.keep <- rowData(sce)$ave.counts > 0
sce <- sce[to.keep,]
summary(to.keep)
## Mode FALSE TRUE
## logical 2 19802
dim(sce)
## [1] 19802 2989
After quality control and filtering, one should normalize the dataset to remove potential technical bias. One strategy is to use CPM (Count Per Million) normalization which is a correction to remove the noise related to sequencing depth. CPM divides each count by its total sum (across all the genes) and multiplies by one million. In this way, each cell has the same total sum of the counts.
cpm(sce) <- calculateCPM(sce, use_size_factors=FALSE)
cpm(sce)[1:4,1:4]
## V3 V4 V5 V6
## Tspan12 0.0000 0.00000 0.000 94.75081
## Tshz1 144.5226 47.89501 0.000 63.16720
## Fnbp1l 144.5226 47.89501 197.336 126.33441
## Adamts15 0.0000 0.00000 0.000 0.00000
Another strategy, more suitable for scRNAseq data, is deconvolution method implemented in scran package. The deconvolution method normalizes data by cells-pooled size factors that account for dropout biases. More details about this normalization technique can be found in study https://genomebiology.biomedcentral.com/articles/10.1186/s13059-016-0947-7.
set.seed(100)
clusters <- quickCluster(sce, min.size=200, min.mean=0.1, method="igraph")
sce <- computeSumFactors(sce, cluster=clusters, min.mean=0.1)
sce <- scater::normalize(sce, return_log = FALSE)
normcounts(sce)[1:4,1:4]
## V3 V4 V5 V6
## Tspan12 0.000000 0.0000000 0.000000 1.278561
## Tshz1 1.894734 0.6425685 0.000000 0.852374
## Fnbp1l 1.894734 0.6425685 2.582446 1.704748
## Adamts15 0.000000 0.0000000 0.000000 0.000000
To deal with a large number of dimensions in scRNAseq dataset a feature selection or dimension reduction strategies can be applied.
Feature selection step is aimed at preserving biologically relevant information in the dataset and improving computational efficiency of downstream analyses. In most of the cases, it seeks for highly variable genes based on mean/variance relationship. M3Drop is one of the packages for automatic detection of most variable genes. It first calculates the mean and square coefficient of variation and fits the quadratic curve between the two variables. Then a chi-square test is used to find high variable genes which are significantly above the curve. We found CPM adjusted counts more appriopriate to use as input for this method rather than scran normalized values.
hvg_genes <- BrenneckeGetVariableGenes(cpm(sce), fdr = 0.01, minBiolDisp = 0.5)
length(hvg_genes)
## [1] 8183
sce_sub=sce[hvg_genes,]
Dimension reduction can be used to visualize basic structure of the data or to reduce the number of features prior downstream analysis such as clustering. In contrast to the feature selection which extracts the most informative features, dimension reduction techniques projects the data into a new low-dimensional space that preserves the structure of the data. To reduce dataset dimensionality one can use PCA, tSNE or UMAP techniques implemented in scater package. Note that projections should be applied to normalized and log-transformed count matrices. PCA is a deterministic approach while tSNE and UMAP are stochastic - for reproducibility of tSNE and UMAP projections one has to set the seed for generating random variables.
assay(sce, "logcounts") <- log2(normcounts(sce)+1)
plotPCA(sce, colour_by="level1class", run_args=c(exprs_values = "logcounts"))
## Warning: 'add_ticks' is deprecated.
## Use '+ geom_rug(...)' instead.
set.seed(100)
plotTSNE(sce, colour_by="level1class", run_args=c(exprs_values = "logcounts"))
## Warning: 'add_ticks' is deprecated.
## Use '+ geom_rug(...)' instead.
plotUMAP(sce, colour_by="level1class", run_args=c(exprs_values = "logcounts"))
## Warning: 'add_ticks' is deprecated.
## Use '+ geom_rug(...)' instead.
How the projections would change if you would plot raw or CPM counts followed by log-transformation?
To cluster cells we can use SC3 package. SC3 is a consensus clustering method for single-cell RNA-seq data. It first calculates distances between the cells using three metrics: Euclidean, Pearson, and Spearman. On each of the obtained distance matrix two transformations are applied: PCA and Laplacian graph. Then K-Means clustering technique is used to cluster the transformed distance matrices subject to the first d eigenvectors. In result, several individual clusterings are obtained which are further combined into a single consensus clustering using Cluster-based Similarity Partitioning Algorithm (CSPA). Note that when using SC3, one can first estimate the number of populations to be used in the clustering.
sce <- sc3_estimate_k(sce)
## Estimating k...
k_input <- metadata(sce)$sc3$k_estimation
k_input #number of estimated clusters
## [1] 32
rowData(sce)$feature_symbol <- rownames(sce)
sce <- sc3(sce, ks = k_input, gene_filter = FALSE, biology = TRUE)
## Setting SC3 parameters...
## Your dataset contains more than 2000 cells. Adjusting the nstart parameter of kmeans to 50 for faster performance...
## Calculating distances between the cells...
## Performing transformations and calculating eigenvectors...
## Performing k-means clustering...
## Calculating consensus matrix...
## Calculating biology...
table(colData(sce)[paste0("sc3_",k_input, "_clusters")][[1]])
##
## 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
## 253 94 92 69 276 10 2 40 69 108 12 9 141 4 60 11 85 66
## 19 20 21 22 23 24 25 26 27 28 29 30 31 32
## 21 71 8 83 322 113 136 66 27 300 29 344 44 24
If the cell annotation is available, one can measure the effectiveness of clustering output using Adjusted Rand Index (ARI). ARI index measures similarity between the partition obtained from clustering and the partition obtained from dataset annotation. The values of the ARI range can be negative if the agreement of the partitions is worse then the agreement expected by chance, or between 0 and 1 for clustering better then chance. ARI close to 1 indicate high accuracy of the method in detecting annotated cell populations.
adjustedRandIndex(colData(sce)[paste0("sc3_",k_input, "_clusters")][[1]],sce$level1class)
## [1] 0.3084636
How the clustering would change if you would use true number of cell populations (taken from annotation) in the inference of SC3?
You can use colour_by argument to color points in any of the above projections by a selected marker gene or any information stored in the annotation. Additional violin plots allow you to i.e. visualize the expression of the selected gene across all the cells from a given cell type. For the illustrative purpose, we used Gad1 gene taken from the literature, which is a marker for interneuron cell type in the cortex tissue.
set.seed(100)
plotTSNE(sce, colour_by="Gad1", run_args=c(exprs_values = "logcounts")) #marker for interneurons
## Warning: 'add_ticks' is deprecated.
## Use '+ geom_rug(...)' instead.
plotExpression(sce, "Gad1", x = "level1class", colour_by = "level1class", exprs_values = "logcounts")
Seurat is an R package designed for QC, analysis, and exploration of single-cell RNA-seq data. All procedures are based on Seurat object which can store the information about counts and data annotation. Note that when creating new Seurat object one can perform cell and gene filtering based on specified thresholds. Here we set both filterings to 0, as we will input already quality controlled and filtered counts.
seur <- CreateSeuratObject(raw.data = counts(sce), meta.data = data.frame(colData(sce)), min.cells = 0, min.genes = 0)
seur
## An object of class seurat in project SeuratProject
## 19802 genes across 2989 samples.
Seurat also provides function to normalize data by scaling factor followed by log-transformation.
seur <- NormalizeData(seur, normalization.method = "LogNormalize")
seur@data[1:4,1:4]
## 4 x 4 sparse Matrix of class "dgCMatrix"
## V3 V4 V5 V6
## Tspan12 . . . 0.6665506
## Tshz1 0.8941375 0.3913325 . 0.4896053
## Fnbp1l 0.8941375 0.3913325 1.089693 0.8168434
## Adamts15 . . . .
For extracting most informative genes one can use FindVariableGenes function. Similarly to other feature selection strategies FindVariableGenes selects informative features beased on mean / dispersion relation.
seur <- FindVariableGenes(object = seur)
Scales and centers genes in the dataset.
seur<- Seurat::ScaleData(object = seur)
## Scaling data matrix
PCA or ICA (Independent Components Analysis), both with a user-specified or the internal number of reduced dimensions () (the internal value is equal to the full number of features). Then, on the data reduced space, K-nearest neighbor (KNN) graph is constructed (based on the Euclidean distances between any pair of cells) and weighted by the shared overlap in the local neighborhoods of the nodes. to cluster cells represented in such graph structure, uses modularity detection Louvain algorithm (herein called ) that estimates the number of clusters by optimizing the modularity function .
seur <- RunPCA(object = seur, pc.genes=seur@var.genes)
seur <- FindClusters(object = seur, reduction.type="pca")
table(seur@ident)
##
## 0 1 2 3 4 5 6 7
## 853 666 372 311 276 246 183 82
Seurat::DimPlot(seur, group.by = "ident", reduction.use = "pca")
Seurat::DimPlot(seur, group.by = "level1class", reduction.use = "pca")
adjustedRandIndex(as.numeric(seur@ident), seur@meta.data$level1class)
## [1] 0.7844589
CellDataSet data object is similar for SingleCellExperiment object. It stores information about the experiment (i.e. expression matrix and metadata) in “slots”. Note that when creating a new CellDataSet data should not be normalized - Monocle, when performing downstream analysis steps, normalizes the data internally.
rowData(sce)$gene_short_name <- rownames(sce)
pd <- new("AnnotatedDataFrame", data = data.frame(colData(sce))) #cell info
fd <- new("AnnotatedDataFrame", data = data.frame(rowData(sce))) #gene info
cds <- newCellDataSet(cellData = counts(sce), phenoData = pd, featureData = fd, expressionFamily=VGAM::negbinomial.size())
cds
## CellDataSet (storageMode: environment)
## assayData: 19802 features, 2989 samples
## element names: exprs
## protocolData: none
## phenoData
## sampleNames: V3 V4 ... V3007 (2989 total)
## varLabels: tissue group ... Size_Factor (58 total)
## varMetadata: labelDescription
## featureData
## featureNames: Tspan12 Tshz1 ... Gm21943 (19802 total)
## fvarLabels: is_feature_control is_feature_control_Spike ...
## gene_short_name (17 total)
## fvarMetadata: labelDescription
## experimentData: use 'experimentData(object)'
## Annotation:
cds <- BiocGenerics::estimateSizeFactors(cds)
cds <- estimateDispersions(cds)
## Removing 129 outliers
disp <- dispersionTable(cds)
head(disp)
## gene_id mean_expression dispersion_fit dispersion_empirical
## 1 Tspan12 0.27716042 5.633211 10.023476
## 2 Tshz1 0.41719267 3.973567 8.826994
## 3 Fnbp1l 0.83724181 2.325529 3.491744
## 4 Adamts15 0.04898606 28.664478 83.258508
## 5 Cldn12 0.30621583 5.164049 5.294964
## 6 Rxfp1 0.03631543 38.425363 12.890793
hvg <- subset(disp, mean_expression >= 0.5 & dispersion_empirical >= 0.1)
cds <- setOrderingFilter(cds, ordering_genes = hvg$gene_id)
plot_ordering_genes(cds)
## Warning: Transformation introduced infinite values in continuous y-axis
cds <- cds[hvg$gene_id,]
cds <- reduceDimension(cds, max_components = 2, reduction_method = 'tSNE', verbose = T)
## Warning in if (method == "PCA") {: the condition has length > 1 and only
## the first element will be used
cds <- clusterCells(cds, num_clusters = NULL)
## Distance cutoff calculated to 3.26211
p1 <- monocle::plot_cell_clusters(cds, color_by = 'Cluster')
p1
p2 <- monocle::plot_cell_clusters(cds, color_by = 'level1class')
p2
adjustedRandIndex(cds$Cluster,cds$level1class)
## [1] 0.6961306
sessionInfo()
## R version 3.5.1 (2018-07-02)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 16.04.3 LTS
##
## Matrix products: default
## BLAS: /usr/lib/atlas-base/atlas/libblas.so.3.0
## LAPACK: /usr/lib/atlas-base/atlas/liblapack.so.3.0
##
## locale:
## [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
## [3] LC_TIME=en_GB.UTF-8 LC_COLLATE=en_US.UTF-8
## [5] LC_MONETARY=en_GB.UTF-8 LC_MESSAGES=en_US.UTF-8
## [7] LC_PAPER=en_GB.UTF-8 LC_NAME=C
## [9] LC_ADDRESS=C LC_TELEPHONE=C
## [11] LC_MEASUREMENT=en_GB.UTF-8 LC_IDENTIFICATION=C
##
## attached base packages:
## [1] parallel stats4 stats graphics grDevices utils datasets
## [8] methods base
##
## other attached packages:
## [1] SC3_1.10.1 scran_1.10.2
## [3] mclust_5.4.3 Seurat_2.3.4
## [5] cowplot_0.9.4 monocle_2.99.2
## [7] L1Graph_0.1.1 lpSolveAPI_5.5.2.0-17
## [9] DDRTree_0.1.5 irlba_2.3.3
## [11] igraph_1.2.4 Matrix_1.2-17
## [13] M3Drop_1.8.1 numDeriv_2016.8-1
## [15] scater_1.10.1 ggplot2_3.1.0
## [17] SingleCellExperiment_1.4.1 SummarizedExperiment_1.12.0
## [19] DelayedArray_0.8.0 BiocParallel_1.16.6
## [21] matrixStats_0.54.0 Biobase_2.42.0
## [23] GenomicRanges_1.34.0 GenomeInfoDb_1.18.2
## [25] IRanges_2.16.0 S4Vectors_0.20.1
## [27] BiocGenerics_0.28.0
##
## loaded via a namespace (and not attached):
## [1] prabclus_2.2-7 R.methodsS3_1.7.1
## [3] coda_0.19-2 pkgmaker_0.27
## [5] tidyr_0.8.3 acepack_1.4.1
## [7] bit64_0.9-7 knitr_1.22
## [9] R.utils_2.8.0 data.table_1.12.0
## [11] rpart_4.1-13 RCurl_1.95-4.12
## [13] doParallel_1.0.14 metap_1.1
## [15] snow_0.4-3 RANN_2.6.1
## [17] VGAM_1.1-1 combinat_0.0-8
## [19] proxy_0.4-23 future_1.12.0
## [21] bit_1.1-14 webshot_0.5.1
## [23] httpuv_1.5.0 assertthat_0.2.1
## [25] viridis_0.5.1 xfun_0.5
## [27] evaluate_0.13 promises_1.0.1
## [29] DEoptimR_1.0-8 caTools_1.17.1.2
## [31] DBI_1.0.0 htmlwidgets_1.3
## [33] sparsesvd_0.1-4 spdep_1.0-2
## [35] purrr_0.3.2 RSpectra_0.13-1
## [37] crosstalk_1.0.0 dplyr_0.8.0.1
## [39] backports_1.1.3 trimcluster_0.1-2.1
## [41] gbRd_0.4-11 deldir_0.1-16
## [43] Cairo_1.5-10 ROCR_1.0-7
## [45] withr_2.1.2 robustbase_0.93-4
## [47] checkmate_1.9.1 cluster_2.0.7-1
## [49] ape_5.3 segmented_0.5-3.0
## [51] lazyeval_0.2.2 crayon_1.3.4
## [53] hdf5r_1.1.1 labeling_0.3
## [55] glmnet_2.0-16 edgeR_3.24.3
## [57] pkgconfig_2.0.2 slam_0.1-45
## [59] units_0.6-2 nlme_3.1-137
## [61] vipor_0.4.5 nnet_7.3-12
## [63] rlang_0.4.0 globals_0.12.4
## [65] diptest_0.75-7 miniUI_0.1.1.1
## [67] registry_0.5-1 doSNOW_1.0.16
## [69] ggrastr_0.1.7 lmtest_0.9-36
## [71] rngtools_1.3.1 Rhdf5lib_1.4.3
## [73] boot_1.3-20 zoo_1.8-5
## [75] base64enc_0.1-3 beeswarm_0.2.3
## [77] ggridges_0.5.1 pheatmap_1.0.12
## [79] png_0.1-7 viridisLite_0.3.0
## [81] bitops_1.0-6 R.oo_1.22.0
## [83] KernSmooth_2.23-15 DelayedMatrixStats_1.4.0
## [85] rgl_0.100.19 doRNG_1.7.1
## [87] classInt_0.3-1 lars_1.2
## [89] stringr_1.4.0 manipulateWidget_0.10.0
## [91] scales_1.0.0 magrittr_1.5
## [93] plyr_1.8.4 ica_1.0-2
## [95] gplots_3.0.1.1 bibtex_0.4.2
## [97] gdata_2.18.0 zlibbioc_1.28.0
## [99] compiler_3.5.1 HSMMSingleCell_1.2.0
## [101] lsei_1.2-0 bbmle_1.0.20
## [103] RColorBrewer_1.1-2 rrcov_1.4-7
## [105] fitdistrplus_1.0-14 dtw_1.20-1
## [107] XVector_0.22.0 LearnBayes_2.15.1
## [109] listenv_0.7.0 pbapply_1.4-0
## [111] htmlTable_1.13.1 Formula_1.2-3
## [113] MASS_7.3-51.3 tidyselect_0.2.5
## [115] stringi_1.4.3 densityClust_0.3
## [117] yaml_2.2.0 locfit_1.5-9.1
## [119] latticeExtra_0.6-28 ggrepel_0.8.0
## [121] pbmcapply_1.3.1 grid_3.5.1
## [123] tools_3.5.1 rstudioapi_0.10
## [125] foreach_1.4.4 foreign_0.8-71
## [127] gridExtra_2.3 Rtsne_0.15
## [129] digest_0.6.18 FNN_1.1.3
## [131] shiny_1.2.0 qlcMatrix_0.9.7
## [133] fpc_2.1-11.1 Rcpp_1.0.1
## [135] SDMTools_1.1-221 later_0.8.0
## [137] WriteXLS_4.1.0 httr_1.4.0
## [139] sf_0.7-3 npsurv_0.4-0
## [141] kernlab_0.9-27 Rdpack_0.10-1
## [143] colorspace_1.4-1 reticulate_1.11.1
## [145] umap_0.2.0.0 splines_3.5.1
## [147] statmod_1.4.30 expm_0.999-4
## [149] sp_1.3-1 flexmix_2.3-15
## [151] plotly_4.8.0 spData_0.3.0
## [153] xtable_1.8-3 jsonlite_1.6
## [155] dynamicTreeCut_1.63-1 modeltools_0.2-22
## [157] R6_2.4.0 gmodels_2.18.1
## [159] Hmisc_4.2-0 pillar_1.4.2
## [161] htmltools_0.3.6 mime_0.6
## [163] glue_1.3.1 BiocNeighbors_1.0.0
## [165] class_7.3-15 codetools_0.2-16
## [167] pcaPP_1.9-73 tsne_0.1-3
## [169] mvtnorm_1.0-10 lattice_0.20-38
## [171] tibble_2.1.1 mixtools_1.1.0
## [173] ggbeeswarm_0.6.0 gtools_3.8.1
## [175] survival_2.44-1.1 limma_3.38.3
## [177] rmarkdown_1.12 docopt_0.6.1
## [179] fastICA_1.2-1 munsell_0.5.0
## [181] e1071_1.7-1 rhdf5_2.26.2
## [183] GenomeInfoDbData_1.2.0 iterators_1.0.10
## [185] HDF5Array_1.10.1 reshape2_1.4.3
## [187] gtable_0.3.0